/*
****************************************************************************************
** breif: LCD板载初始化
**
** Author: xiongjinqi
** Date:  2022/11/30
** History: 
        2022/11/30: 初始版本
****************************************************************************************
*/
#define LOG_TAG          "bsplcd"
#include <elog.h>

#include <stdio.h>
#include <string.h>
#include "bsp_lcd.h"
#include "delay.h"
#include "font.h"


void bsplcd_data_io_dir_set(u8 dir)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    if(dir == IO_IN)
    {
        GPIO_InitStruct.Pin = LCD_D0_Pin|LCD_D1_Pin|LCD_D2_Pin|LCD_D3_Pin
                          |LCD_D4_Pin|LCD_D5_Pin|LCD_D6_Pin|LCD_D7_Pin;
        GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    }
    if(dir == IO_OUT)
    {
        GPIO_InitStruct.Pin = LCD_D0_Pin|LCD_D1_Pin|LCD_D2_Pin|LCD_D3_Pin
                          |LCD_D4_Pin|LCD_D5_Pin|LCD_D6_Pin|LCD_D7_Pin;
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    }

}


/* 
    @breif: transfer command to LCD
*/
static void bsplcd_command_transfer(u8 cmd)
{
    LCD_CS_SET(0);
    LCD_A0_SET(0);
    LCD_RD_SET(1);
    delay_us(1);
    LCD_WR_SET(0);
    LCD_WRITE_DATA(cmd);
    LCD_WR_SET(1);
    delay_us(1);
    LCD_CS_SET(1);
    LCD_A0_SET(1);
}

/* 
    @breif: transfer data to LCD
*/
static void bsplcd_data_transfer(int data)
{
    
    LCD_DATA_IO_OUT();//设置LCD数据总线方向
    delay_us(1);
    
    LCD_CS_SET(0);
    LCD_A0_SET(1);
    LCD_RD_SET(1);
    delay_us(1);
    LCD_WR_SET(0);
    LCD_WRITE_DATA(data);
    LCD_WR_SET(1);
    delay_us(1);
    LCD_CS_SET(1);
    LCD_A0_SET(1);
}
/* 
    @breif: recieve data from LCD
    @x  : 起始列
    @len: 读多少列
*/
static u8 bsplcd_data_receiver(u8 *data,u16 x, u16 len)
{

    u8 dummy = 0;
    delay_us(1);
    LCD_CS_SET(0);
    LCD_A0_SET(1);
    LCD_RD_SET(1);
    delay_us(1);
    
    LCD_WR_SET(0);  // WR下降沿
    LCD_WRITE_DATA(x);
    delay_us(1);
    LCD_WR_SET(1); // WR上升沿,MPU写数据到DDRAM
    LCD_DATA_IO_IN();   // 设置LCD DATA IO为输入方向
    
    LCD_RD_SET(0);  // RD下降沿，data from on-chip RAM to the MPU
    dummy = LCD_READ_DATA();
    LCD_RD_SET(1); // RD上降沿， data from the MPU to data bus
    delay_us(1);

    for(u16 inx = 0; inx<len; inx++)
    {
        LCD_RD_SET(0);
        *(data+inx) = LCD_READ_DATA();
        LCD_RD_SET(1);
        delay_us(1);
    }
    
    LCD_CS_SET(1);
    LCD_A0_SET(1);
    return data;
}

static void bsplcd_data_write(u8 *buff, u8 buf_len)
{
    for(u8 index = 0; index < buf_len; index++)
        bsplcd_data_transfer(buff[index]);
};


void bsplcd_init(void)
{
    LCD_RST_SET(0);
    delay_ms(100);
    LCD_RST_SET(1);
    delay_ms(100);
   
    
    bsplcd_command_transfer(0x30);   //EXT=0
    bsplcd_command_transfer(0x94);   //Sleep out
    bsplcd_command_transfer(0x31);   //EXT=1
    bsplcd_command_transfer(0xD7);   //Autoread disable
    bsplcd_data_transfer(0X9F);      //Disable auto read

    bsplcd_command_transfer(0x32);   //Analog circiit SET
    bsplcd_data_transfer(0x00);      //OSC Frequency adjustment
    bsplcd_data_transfer(0x01);      //Frequency on booster capacitors->6KHz
    bsplcd_data_transfer(0x04);      //Bias=1/10

    bsplcd_command_transfer(0x20);   //
    bsplcd_data_transfer(0x01); 
    bsplcd_data_transfer(0x03);
    bsplcd_data_transfer(0x05);
    bsplcd_data_transfer(0x07); 
    bsplcd_data_transfer(0x09);
    bsplcd_data_transfer(0x0b);
    bsplcd_data_transfer(0x0d);
    bsplcd_data_transfer(0x10);
    bsplcd_data_transfer(0x11);
    bsplcd_data_transfer(0x13);
    bsplcd_data_transfer(0x15);
    bsplcd_data_transfer(0x17);
    bsplcd_data_transfer(0x19);
    bsplcd_data_transfer(0x1b);
    bsplcd_data_transfer(0x1d);
    bsplcd_data_transfer(0x1f);


    bsplcd_command_transfer(0x31);   // EXT = 1
    bsplcd_command_transfer(0xF0);   // 此指令比较重要 上加此指令升压会慢0.5S  不同温度范围内的帧频率    
    bsplcd_data_transfer(0x0E);
    bsplcd_data_transfer(0x0E);
    bsplcd_data_transfer(0x0E);
    bsplcd_data_transfer(0x0E);


    bsplcd_command_transfer(0x30);   //EXT1=0，EXT0=0,表示选择了“扩展指令表1”
    bsplcd_command_transfer(0x75);   //页地址设置
    bsplcd_data_transfer(0x00);      //起始页地址：YS=0X00
    bsplcd_data_transfer(0x28);      //结束页地址：YE=0x1F   每4行为一页，第0～3行为第0页，第124～127行为第31页（31=0x1f）
    bsplcd_command_transfer(0x15);   //列地址设置
    bsplcd_data_transfer(0x00);      //起始列地址：XS=0
    bsplcd_data_transfer(0x9F);      //结束列地址：XE=160（0xef）


    bsplcd_command_transfer(0xBC);      //数据扫描方向
    bsplcd_data_transfer(0x00);         //MX.MY=Normal


    bsplcd_data_transfer(0xA6);         // 数据不反显 正常显示
//  bsplcd_command_transfer(0x0c);      //数据格式选择,0x0C是低位在前D0-D7,0x08是高位在前D7-D0


    bsplcd_command_transfer(0xCA);      //显示控制
    bsplcd_data_transfer(0x00);         //设置CL驱动频率：CLD=0 
    bsplcd_data_transfer(0x9F);         //占空比：Duty=160
    bsplcd_data_transfer(0x20);         //N行反显关闭：Nline=off


    bsplcd_command_transfer(0xF0);      //显示模式 
    bsplcd_data_transfer(0x10);         //如果设为0x11：表示选择4灰度级模式，如果设为0x10:表示选择黑白模式


    bsplcd_command_transfer(0x81);      //设置对比度，“0x81”不可改动，紧跟着的2个数据是可改的，但“先微调后粗调”这个顺序别乱了
    bsplcd_data_transfer(0x0E);         //微调对比度,可调范围0x00～0x3f，共64级
    bsplcd_data_transfer(0x04);         //粗调对比度,可调范围0x00～0x07，共8级
    bsplcd_command_transfer(0x20);      //电源控制
    bsplcd_data_transfer(0x0B);         //D0=regulator ; D1=follower ; D3=booste,  on:1 off:0
    delay_us(100);
    bsplcd_command_transfer(0xAF);      //打开显示
    LCD_BLK_ENABLE();                   //打开背光
    bsplcd_clear_fullscreen();          //清屏
}

/*
设置LCD 行列地址：
    X为起始的列地址
    Y为起始的行地址
    x_total为结束列地址与起始列地址的差值
    y_total为结束行地址与起始行地址的差值
*/
void bsplcd_address(u8 x, u8 y, u8 x_total, u8 y_total)
{
    u8 end_y = 0;
    end_y = y_total; // /4;
    
    bsplcd_command_transfer(0x15); //Set Column Address
    bsplcd_data_transfer(x);
    bsplcd_data_transfer(x+x_total-1);

    bsplcd_command_transfer(0x75); //Set Page Address
    bsplcd_data_transfer(y);
    bsplcd_data_transfer(y+end_y-1);
    bsplcd_command_transfer(0x30); 
    bsplcd_command_transfer(0x5c); 
}

/*
    brief: 设置灰度值，必须在4灰度模式下
    light: 浅灰
    dark : 深灰
*/
void bsplcd_gray_level_set(u8 light, u8 dark)
{
    bsplcd_command_transfer(0x31); // Extension Command 2
    bsplcd_command_transfer(0x20); // Set Gray Scale Level
    bsplcd_data_transfer(0x00);
    bsplcd_data_transfer(0x00);
    bsplcd_data_transfer(0x00);
    
    bsplcd_data_transfer(light);
    bsplcd_data_transfer(light);
    bsplcd_data_transfer(light);

    bsplcd_data_transfer(0x00);
    bsplcd_data_transfer(0x00);

    bsplcd_data_transfer(dark);
    bsplcd_data_transfer(0x00);
    bsplcd_data_transfer(0x00);
    bsplcd_data_transfer(dark);
    bsplcd_data_transfer(dark);
    bsplcd_data_transfer(dark);
    
    bsplcd_data_transfer(0x00);
    bsplcd_data_transfer(0x00);
}

void bsplcd_display_onoff(u8 sta)
{
    if(sta)
    {
        bsplcd_command_transfer(0xAF);
    }
    else
    {
        bsplcd_command_transfer(0xAE); 
    }
}
/*
    MV: Address-scan direction. MV =0: In the column direction, MV =1: In the page direction
    MX: Normal/reverse direction of the column address. MX =0: Normal, MX =1: Reverse
*/
void bsplcd_scan_dir(u8 dir)
{
    // 固定到初始化中

    /* 列方向(Column Direction)*/
    bsplcd_command_transfer(0xBC);      //数据扫描方向
    bsplcd_data_transfer(0x00);         //MX.MY=Normal

}
void bsplcd_drawpoint(u16 x, u16 y, u16 ptval,u16 color)
{
    //画显示区域
    bsplcd_address(x,y,x,y);
    
    //画点
    bsplcd_data_transfer(ptval);
}


/*清屏*/
void bsplcd_clear_fullscreen(void)
{
    u8 i,j;
    bsplcd_address(0,0,160,20);
    for(i=0;i<20;i++)
    {
        for(j=0;j<160;j++)
        {
            bsplcd_data_transfer(0x00);
        }
    }
}

/*************************************************************************/
/* 以下为：驱动层代码 ****************************************************/
/*************************************************************************/

///*显示160*80点阵的图像*/
void disp16080(u8 *dp,u8 startC, u8 startP)
{
    u8 i,j;
    bsplcd_command_transfer(0x08);    //高位在前
    bsplcd_address(startC,startP,160,20);
    for(i=0;i<10;i++) // 行, 最大值20
    {
        for(j=0;j<160;j++) // 列
        {
            bsplcd_data_transfer(*dp++);
        }
    }
}

///*显示32*32点阵的图像*/
void disp3232(u8 *dp,u8 startC, u8 startP)
{
    u8 i,j;
    bsplcd_command_transfer(0x08);    //高位在前
    bsplcd_address(startC,startP, 32, 4);
    for(i=0;i<4;i++) // 行, 最大值20
    {
        for(j=0;j<32;j++) // 列
        {
            bsplcd_data_transfer(*dp++);
        }
    }
}

/*
    测试读ddram中的数据
        X为起始的列地址
        Y为起始的行地址
        x_total为结束列地址与起始列地址的差值
        y_total为结束行地址与起始行地址的差值

*/
void display_ddram_read(u8 x, u8 x_total, u8 y, u8 y_total)
{
    u8 ddram[160*10] = {0};

    bsplcd_address(x, y, x_total, y_total);
    bsplcd_command_transfer(0x5D);
    printf("Read DDRAM data:\r\n");
 
    bsplcd_data_receiver(&ddram, x, x_total*y_total); // 读x_total个数据
    for(u16 inz = 0; inz<x_total*y_total; inz++)
    {
        if(inz%x_total == 0)
            printf("\r\n");
        printf("%02x ", ddram[inz]);
        
    }
    printf("\r\n");
    memset(&ddram, 0 , x_total*y_total); // 清缓存
}
/*
    @brief: 按照行反白
    
*/
void whiteword(u8 x, u8 x_total, u8 y, u8 y_total)
{
    u8 ddram[160*10] = {0};
    u8 tmp = 0;
    bsplcd_address(x, y, x_total, y_total);
    bsplcd_command_transfer(0x5D);

    printf("Read DDRAM data:\r\n");

    bsplcd_data_receiver(&ddram, x, x_total*y_total); // 读x_total个数据
    for(u16 inz = 0; inz<x_total*y_total; inz++)
    {
        if(inz%x_total == 0)
            printf("\r\n");
        printf("%02x ", ddram[inz]);
        
    }
    printf("\r\n");

    // 反白
    for(u16 inx = 0; inx < x_total*y_total; inx++)
    {
        tmp = ~ddram[inx];
        ddram[inx] = tmp;
    }
    bsplcd_address(x, y_total, 16, 2); // 下一行显示反白
    bsplcd_data_write(&ddram, x_total*y_total);
    memset(&ddram, 0 , x_total*y_total); // 清缓存

}
/*
//------------------显示方框-----------------------
x:起始列,范围0-160
ex:终止列,范围0-160

y:起始行,范围0-20
ey:终止行,范围0-20
*/
void lcd_kuang2(u16 x, u16 ex, u16 y,  u16 ey)
{
    // todo: 判断终点ex ey是否超出显示区域
    #if 1
    // add code
    if(ex >= 160)
    {
        ex = 159;
    }
    if(ey > 20)
    {
        ey = 20;
    }
    #endif
    // 画水平线
    for(u8 iny = y; iny<ey; iny++)
    {
        if(iny==y)
        {
            for(u8 inx = x; inx < ex; inx++)
            {
                bsplcd_drawpoint(inx, iny, 0x80, NULL);
            }
        }
        if(iny == (ey-1))
        {
            for(u8 inx = x; inx < ex; inx++)
            {
                if(ey == 1)
                {
                    bsplcd_drawpoint(inx, iny, 0x81, NULL);
                }
                else
                {
                    bsplcd_drawpoint(inx, iny, 0x01, NULL);
                }
            }
        }
    }

    // 画垂直线
    for(u8 iny = y; iny < ey; iny++)
    {
        u8 inx = x;
        bsplcd_drawpoint(inx, iny, 0XFF, NULL);
        bsplcd_drawpoint(ex, iny, 0XFF, NULL);
    }
}
//------------------显示一个汉字16x16点阵-------------------------
void display_hanzi(u8 *p, u8 startP, u8 startC)
{

    u8 i, j;
    bsplcd_command_transfer(0x08); // 高位在前
    bsplcd_address(startC, startP, 16, 2);
    for (i = 0; i < 2; i++)
    {
        for (j = 0; j < 16; j++)
        {
            bsplcd_data_transfer(*p++); // 发送数据
        }
    }
}

//------------------显示一个汉字32x32点阵-------------------------
void display_hanzi32(u8 *p, u8 startP, u8 startC)
{
    u8 i, j;
    bsplcd_command_transfer(0x08); // 高位在前
    bsplcd_address(startC, startP, 32, 4);
    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < 32; j++)
        {
            bsplcd_data_transfer(*p++); // 发送数据
        }
    }
}

////------------------显示8X16点阵字符串-------------------------
void disp_ascii0816(char *ptrAscii, u8 startP, u8 startC)
{

    u8 i, j;
    u16 index;
    bsplcd_command_transfer(0x08); // 高位在前
    while ((*ptrAscii) != '\0')
    {
        index = ((*ptrAscii) - 0x20) * 16;
        bsplcd_address(startC, startP, 8, 2);

        for (i = 0; i < 2; i++)
        {
            for (j = 0; j < 8; j++)
            {
                bsplcd_data_transfer(AsciiDot0816[index++]); // 发送数据
            }
        }
        startC += 8;
        ptrAscii++;
    }
}

////------------------显示5X8点阵字符串-------------------------
void disp_ascii0508(char *ptrAscii, u8 startP, u8 startC)
{

    u8 j;
    u16 index;
    bsplcd_command_transfer(0x08); // 高位在前
    while ((*ptrAscii) != '\0')
    {
        index = ((*ptrAscii) - 0x20) * 5;
        bsplcd_address(startC, startP, 5, 1);
        for (j = 0; j < 5; j++)
        {
            bsplcd_data_transfer(AsciiDot0508[index++]); // 发送数据
        }
        startC += 5;
        ptrAscii++;
    }
}

/* BSP LCD TEST *******************************************************************/
void drawpoint_test(void)
{
    u8 x = 4, y = 1;
    u8 ex = 120, ey = 10;
    //#if 1
    // 画水平线
    for(u8 iny = y; iny<ey; iny++)
    {
        if(iny==y || iny == (ey-1))
        {
            for(u8 inx = x; inx < ex; inx++)
            {
                bsplcd_drawpoint(inx, iny, 0x80, NULL);
            }
        }
    }
    //#else
    // 画垂直线
    for(u8 iny = y; iny < ey; iny++)
    {
        u8 inx = x;
        if(iny == (ey-1))
        {
            bsplcd_drawpoint(inx, iny, 0X80, NULL);
            bsplcd_drawpoint(ex, iny, 0X80, NULL);
        }
        else
        {
            bsplcd_drawpoint(inx, iny, 0XFF, NULL);
            bsplcd_drawpoint(ex, iny, 0XFF, NULL);
        }
    }
    //#endif
}

void display_test(u8 data1,u8 data2)
{
    int i,j ;
    bsplcd_address(0,0,160,20);

    for(i=0;i<20;i++)
    {
        for(j=0;j<80;j++)
        {
            bsplcd_data_transfer(data1);
            bsplcd_data_transfer(data2);
        }
    }
}

void bsp_lcd_test(void)
{
    u8 i;
    #if 0
    u8 tdata[][2]=
    {
        {0xff,0xff},    // All
        {0x00,0x00},    // None 
        {0x00,0xFF},    // Virtical 
        {0xff,0x00},    // Virtical
        {0xAA,0xAA},    // Horizontal 
        {0x55,0x55},    // Horizontal 
        {0xAA,0x55},    // Stars 
        {0x55,0xaa},    // Stars 
    };
    for(i=0; i<8; i++)
    {
        display_test(tdata[i][0],tdata[i][1]);
        delay_ms(1000);
        bsplcd_clear_fullscreen();
    }
    #endif

    #if 0
    drawpoint_test();
    delay_ms(3000);
    bsplcd_clear_fullscreen();
    
    lcd_kuang();
    delay_ms(1000);
    bsplcd_clear_fullscreen();
    #endif


    /* 测试画框 */
    //lcd_kuang2(0, 80, 0, 10);
    //delay_ms(1000);
    lcd_kuang2(0, 80, 0, 1);
    delay_ms(1000);
    
    lcd_kuang2(0, 160,0, 20);
    delay_ms(1000);
    
    bsplcd_clear_fullscreen();
    
    /* 测试读ddram,反白 */
    display_hanzi((u8 *)&shen, 0, 0);      // 显示16X16点阵汉字
    /* 读DDRAM数据 */
    //display_ddram_read(0, 16, 0, 2);

    whiteword(0, 16, 0, 2);
    delay_ms(1000);
    bsplcd_clear_fullscreen();

    #if 0
    display_hanzi32(chuan32, 0, 0);  // 显示32X32点阵汉字
    display_hanzi32(hang32, 0, 32);  // 显示32X32点阵汉字
    display_hanzi32(xian32, 0, 64);  // 显示32X32点阵汉字
    display_hanzi32(si32, 0, 96);    // 显示32X32点阵汉字
    display_hanzi32(ping32, 0, 128); // 显示32X32点阵汉字

    display_hanzi32(bing32, 4, 0);  // 显示32X32点阵汉字
    display_hanzi32(kou32, 4, 32);  // 显示32X32点阵汉字
    disp_ascii0816("/", 5, 80);
    display_hanzi32(cuan32, 4, 96); // 显示32X32点阵汉字
    display_hanzi32(kou32, 4, 128); // 显示32X32点阵汉字
    delay_ms(500);
    bsplcd_clear_fullscreen();
    #endif
    #if 0
    display_hanzi((u8 *)&shen, 8, 0);      // 显示16X16点阵汉字
    display_hanzi((u8 *)&zhen, 8, 16);
    display_hanzi((u8 *)&chuan, 8, 32);
    display_hanzi((u8 *)&hang, 8, 48);
    display_hanzi((u8 *)&dian, 8, 64);
    display_hanzi((u8 *)&zi, 8, 80);
    display_hanzi((u8 *)&ke, 8, 96);
    display_hanzi((u8 *)&ji, 8, 112);
    display_hanzi((u8 *)&gong1, 8, 128);
    display_hanzi((u8 *)&si, 8, 144);

    disp_ascii0816("CH160160G--ST75161--", 10, 0);
    disp_ascii0816("CH160160G----COG----", 12, 0);
    display_hanzi((u8 *)&xian, 14, 0);
    display_hanzi((u8 *)&shi, 14, 16);
    display_hanzi((u8 *)&qu, 14, 32);
    disp_ascii0816(":", 14, 48);
    disp_ascii0508("104.0*39.0 mm", 14, 64);
    disp_ascii0508("97.49*32.46 mm", 15, 64);
    display_hanzi((u8 *)&xiong, 14,144);

    disp_ascii0508("S S S S S S S S S S S S S S S S S S ", 16, 0);
    disp_ascii0508(" S S S S S S S S S S S S S S S S S S", 17, 0);
    disp_ascii0508("S S S S S S S S S S S S S S S S S S ", 18, 0);
    disp_ascii0508(" S S S S S S S S S S S S S S S S S S", 19, 0);
    disp_ascii0508("S S S S S S S S S S S S S S S S S S ", 20, 0);
    delay_ms(1000);
    bsplcd_clear_fullscreen();

    disp16080((u8 *)&tupian, 0, 0);
    display_hanzi32((u8 *)&chuan32, 10, 0);  // 显示32X32点阵汉字
    display_hanzi32((u8 *)&hang32, 10, 32);  // 显示32X32点阵汉字
    display_hanzi32((u8 *)&xian32, 10, 64);  // 显示32X32点阵汉字
    display_hanzi32((u8 *)&si32, 10, 96);    // 显示32X32点阵汉字
    display_hanzi32((u8 *)&ping32, 10, 128); // 显示32X32点阵汉字
    display_hanzi32((u8 *)&bing32, 14, 0);   // 显示32X32点阵汉字
    display_hanzi32((u8 *)&kou32, 14, 32);   // 显示32X32点阵汉字
    disp_ascii0816("/", 15, 80);
    display_hanzi32((u8 *)&cuan32, 14, 96); // 显示32X32点阵汉字
    display_hanzi32((u8 *)&kou32, 14, 128); // 显示32X32点阵汉字
    disp_ascii0816("CH160160G--ST75161--", 18, 0);
    delay_ms(1000);
    bsplcd_clear_fullscreen();
    #endif
}




